package com.ynwt.service;

import com.github.sd4324530.fastweixin.api.CustomAPI;
import com.github.sd4324530.fastweixin.api.QrcodeAPI;
import com.github.sd4324530.fastweixin.api.UserAPI;
import com.github.sd4324530.fastweixin.api.enums.QrcodeType;
import com.github.sd4324530.fastweixin.api.response.QrcodeResponse;
import com.github.sd4324530.fastweixin.message.TextMsg;
import com.ynwt.constant.CacheConst;
import com.ynwt.constant.EventType;
import com.ynwt.constant.JmsDestinationName;
import com.ynwt.constant.RedisKey;
import com.ynwt.domain.Event;
import com.ynwt.domain.User;
import com.ynwt.domain.UserEvent;
import com.ynwt.domain.UserEventLock;
import com.ynwt.enums.*;
import com.ynwt.exception.DuplicateEventException;
import com.ynwt.jms.QueueClient;
import com.ynwt.model.BaseEventRequest;
import com.ynwt.model.BaseRequest;
import com.ynwt.model.BaseResponse;
import com.ynwt.model.ycoin.RegRequest;
import com.ynwt.model.ycoin.RegResponse;
import com.ynwt.repository.EventDao;
import com.ynwt.repository.UserEventDao;
import com.ynwt.repository.UserDao;
import com.ynwt.repository.UserEventLockDao;
import com.ynwt.util.FileUtil;
import com.ynwt.util.JsonUtil;
import com.ynwt.util.TimeUtil;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.orm.ObjectOptimisticLockingFailureException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import javax.jms.JMSException;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * Created by huangying on 2017/5/1.
 */
@Service
public class EventServiceImpl implements EventService {

    private final static Logger LOG = LoggerFactory.getLogger(EventServiceImpl.class);

    @Override
    @Transactional
    public boolean subscribe(User user) {

        /**
         * 判断是否有签到事件锁（签到和关注共用一把锁）
         */
        if (userEventLockDao.countByUserOpenidAndType(user.getOpenid(), UserEventLockType.SIGN) < 1) {
            eventService.addUserEventLock(user, UserEventLockType.SIGN);
        }

        Date date = new Date();
        Event event = eventDao.findOne(EventType.SUBSCRIBE);

        //设置jms请求参数
        BaseEventRequest request = new BaseEventRequest();
        request.setUnionid(user.getUnionid());
        request.setType(event.getKey());
        request.setTimestamp(date.getTime());
        request.setYcoin(event.getYcoin());

        BaseResponse response = null;

        LOG.info("method: subscribe. msg: subscribe event jms request: {}", request);

        //jms请求
        try {
            response = JsonUtil.toBean(queueClient.sendAndReceive(request.toString(), JmsDestinationName.EVENT, 20000), BaseResponse.class);
        } catch (JMSException e) {
            LOG.warn("method: subscribe. msg: subscribe event jms exception.", e);
        }

        LOG.info("method: subscribe. msg: subscribe event jms response: {}", response);

        //记录关注事件
        if (null != response && response.isSuccess()) {

            UserEvent userEvent = new UserEvent(user, event);
            userEvent.setDate(date);
            userEvent.setYcoin(event.getYcoin());

            userEventDao.save(userEvent);

            return true;
        } else {
            LOG.warn("method: subscribe. msg: subscribe event fail.");
            return false;
        }

    }

    @Override
    @Transactional
    public void regHuanQiuXiaoHuoBan(String openid) {

        //防止重复注册
        userEventLockDao.findByUserOpenidAndType(openid, UserEventLockType.SIGN);

        LOG.debug("regHuanQiuXiaoHuoBan begin...");

//        try {
//            Thread.sleep(3000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }

        //从微信服务器获取用户信息
        User user = userService.fetchFromTencent(openid);

        //成功从微信服务器获取逸贝
        if (null != user) {

            user.setRegTime(new Date());
            Event event = eventDao.findOne(EventType.REGISTER);

            //判断用户是否已经注册到逸贝系统
            BaseRequest existRequest = new BaseRequest();
            existRequest.setUnionid(user.getUnionid());
            BaseResponse existResponse = null;

            try {
                existResponse = JsonUtil.toBean(queueClient.sendAndReceive(existRequest.toString(), JmsDestinationName.EXIST, 20000), BaseResponse.class);
            } catch (JMSException e) {
                e.printStackTrace();
            }

            LOG.info("method: regHuanQiuXiaoHuoBan. msg: validate if the user is exist, the jms response : \n {}", existResponse);

            //如果用户已经在逸贝系统中（已经成为环球小伙伴），则触发关注服务号的事件
            if (null != existRequest && existResponse.isSuccess()) {
                if (saveNewUser(user, event, true)) {
                    subscribe(user);
                }
            } else {//如果用户不在逸贝系统中（暂未成为环球小伙伴），向逸贝系统发送注册信息

                //发送用户信息到逸贝系统注册用户
                RegRequest regRequest = new RegRequest();

                regRequest.setUnionid(user.getUnionid());
                regRequest.setOpenid(user.getOpenid());
                regRequest.setNickname(user.getNickname());
                regRequest.setHeadImgUrl(user.getHeadImgUrl());
                regRequest.setRegTime(user.getRegTime().getTime());
                regRequest.setYcoin(event.getYcoin());
                regRequest.setProvince(user.getProvince());
                regRequest.setCountry(user.getCountry());
                regRequest.setCity(user.getCity());
                regRequest.setSex(user.getSex().ordinal());

                RegResponse regResponse = null;
                try {
                    regResponse = JsonUtil.toBean(queueClient.sendAndReceive(regRequest.toString(), JmsDestinationName.REG, 20000), RegResponse.class);
                } catch (JMSException e) {
                    LOG.warn("method:regHuanQiuXiaoHuoBan. msg: reg jms exception. warn msg: {}", e);
                }

                //注册成功后，触发关注服务号事件
                if (null != regResponse && regResponse.isSuccess()) {
                    if (saveNewUser(user, event, false)) {
                        subscribe(user);
                    }
                } else {
                    LOG.warn("method: regHuanQiuXiaoHuoBan. msg: register user failure. reg response:\n {}", regResponse);
                }
            }

        } else {// 如果从微信获取用户信息失败
            LOG.info("method: regHuanQiuXiaoHuoBan. the user get from tencent is null.");
        }
    }

    @Override
    @Transactional
    public boolean saveNewUser(User user, Event event, boolean hasRegister) {

        //保存用户到用户表
        userDao.save(user);

        User u = userDao.findOne(user.getOpenid());

        if (!hasRegister) {

            //记录注册事件到用户事件表
            UserEvent userEvent = new UserEvent(user, event);
            userEvent.setDate(user.getRegTime());
            userEvent.setYcoin(event.getYcoin());//如果是刚刚注册，则获得逸贝，否则没有逸贝奖励
            userEventDao.save(userEvent);

            StringBuffer buff = new StringBuffer();
            buff.append("恭喜【")
                    .append(user.getNickname())
                    .append("】同学成为第【")
                    .append(userDao.count())
                    .append("】位环球小伙伴！\n\n")
                    .append("<a href=\"http://mp.weixin.qq.com/s/6K4iLMKdvNIuNlKFNI-ABg\">你由于努力学习被奖励了【")
                    .append(event.getYcoin())
                    .append("】个逸贝哦~</a>");
            customAPI.sendCustomMessage(user.getOpenid(), new TextMsg(buff.toString()));

            //发送注册信息到二维码推广系统
            RegRequest regRequest = new RegRequest();
            regRequest.setUnionid(user.getUnionid());
            regRequest.setHeadImgUrl(user.getHeadImgUrl());
            regRequest.setNickname(user.getNickname());
            regRequest.setRegTime(user.getRegTime().getTime());
            regRequest.setSex(user.getSex().ordinal());
            jmsTemplate.convertAndSend(JmsDestinationName.REGISTER_QRCODE, regRequest.toString());
        }
        if (userEventDao.countByUserOpenidAndEventKey(user.getOpenid(), event.getKey()) > 1) {
            LOG.warn("method: saveNewUser. msg: duplicate save userUser.");
            throw new DuplicateEventException("duplicate register user.");
        } else {
            LOG.debug("method: saveNewUser. msg: save successfully.");
            return true;
        }
    }

    @Override
    @Transactional
    public void handleQrCodeSubscribe(String openid, int scene) {

        //注册称环球小伙伴
        if (!isSubscribe(openid)) {
            regHuanQiuXiaoHuoBan(openid);
        }

//        try {
//            Thread.sleep(1000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }

        //todo 连续助力两次的问题尚未解决

        //获取助力者
        User helper = userDao.findOne(openid);

        //获取发起者
        User launcher = null;//todo 换成下面那句
//        User launcher = userDao.findById(scene);

        Date fireDate = new Date();

        //如果助力者没有助力过
        if (userEventDao.countByUserOpenidAndEventKey(openid, EventType.QRCODE_HELP) < 1) {

            Event eventHelp = eventDao.findOne(EventType.QRCODE_HELP);
            Event eventPromote = eventDao.findOne(EventType.QRCODE_PROMOTE);

            //扫描参数二维码推广：给逸贝系统发送jms消息
            BaseEventRequest eventHelpRequest = new BaseEventRequest();
            BaseEventRequest eventPromoteRequest = new BaseEventRequest();

            eventHelpRequest.setUnionid(helper.getUnionid());
            eventHelpRequest.setYcoin(eventHelp.getYcoin());
            eventHelpRequest.setTimestamp(fireDate.getTime());
            eventHelpRequest.setType(EventType.QRCODE_HELP);

            eventPromoteRequest.setUnionid(launcher.getUnionid());
            eventPromoteRequest.setYcoin(eventPromote.getYcoin());
            eventPromoteRequest.setTimestamp(fireDate.getTime());
            eventPromoteRequest.setType(EventType.QRCODE_PROMOTE);

            BaseResponse eventHelpResponse = null;
            BaseResponse eventPromoteResponse = null;

            try {
                eventHelpResponse = JsonUtil.toBean(queueClient.sendAndReceive(eventHelpRequest.toString(), JmsDestinationName.EVENT, 20000), BaseResponse.class);
                eventPromoteResponse = JsonUtil.toBean(queueClient.sendAndReceive(eventPromoteRequest.toString(), JmsDestinationName.EVENT, 20000), BaseResponse.class);
            } catch (JMSException e) {
                LOG.warn("method: handleQrCodeSubscribe. msg: send and receive jms exception.", e);
            }

            if (null != eventPromoteResponse && null != eventHelpResponse && eventPromoteResponse.isSuccess() && eventHelpResponse.isSuccess()) {
                //新建助力记录和推广记录
                UserEvent userEventHelp = new UserEvent(helper, eventHelp);
                UserEvent userEventPromote = new UserEvent(launcher, eventPromote);

                Date date = new Date();
//                userEventHelp.setType(UserEventType.ADD);
                userEventHelp.setDate(date);
                userEventHelp.setYcoin(eventHelp.getYcoin());
                userEventHelp.setContent(launcher.getOpenid());

//                userEventPromote.setType(UserEventType.ADD);
                userEventPromote.setDate(date);
                userEventPromote.setYcoin(eventPromote.getYcoin());
                userEventPromote.setContent(helper.getOpenid());

                //保存到数据库
                userEventDao.save(userEventHelp);
                userEventDao.save(userEventPromote);

                StringBuffer helpMsg = new StringBuffer();
                StringBuffer lancherMsg = new StringBuffer();
                //助力者消息
                helpMsg.append("<a href=\"http://mp.weixin.qq.com/s/qgHox4kLnnVC2v4mD4ljPw\">你刚刚帮助了你的小伙伴【")
                        .append(launcher.getNickname())
                        .append("】同学，你获得了【")
                        .append(eventHelp.getYcoin())
                        .append("】个逸贝，你的小伙伴获得了【")
                        .append(eventPromote.getYcoin())
                        .append("】个逸贝的奖励~</a>");
                //发起者消息
                lancherMsg.append("<a href=\"http://mp.weixin.qq.com/s/6K4iLMKdvNIuNlKFNI-ABg\">刚刚您的小伙伴【")
                        .append(helper.getNickname())
                        .append("】同学，帮助了您，你获得了【")
                        .append(eventPromote.getYcoin())
                        .append("】个逸贝，你的小伙伴获得了【")
                        .append(eventHelp.getYcoin())
                        .append("】个逸贝的奖励~</a>");
                //发送客服消息
                customAPI.sendCustomMessage(helper.getOpenid(), new TextMsg(helpMsg.toString()));
                customAPI.sendCustomMessage(launcher.getOpenid(), new TextMsg(lancherMsg.toString()));

            } else {
                LOG.warn("method: handleQrCodeSubscribe. msg: jms record qrcode promote event error.");
                customAPI.sendCustomMessage(helper.getOpenid(), new TextMsg("系统开小差啦，稍等一会，取关后再帮你的小伙伴扫一扫哦～"));
                customAPI.sendCustomMessage(launcher.getOpenid(), new TextMsg("系统开小差啦，稍等一会，可以请你的小伙伴【" + helper.getNickname() + "】取关后再帮你扫一扫哦～"));
            }

        } else {
            customAPI.sendCustomMessage(helper.getOpenid(), new TextMsg("你已经助力过了，每个人只能助力一次哦～"));
            customAPI.sendCustomMessage(launcher.getOpenid(), new TextMsg("你的好友【" + helper.getNickname() + "】已经助力一次，不能再给你助力了哦～"));
        }

    }

    @Override
    public void createQrcode(String openid) {
        User user = userDao.findOne(openid);

        //二维码为null或者为空时，才会创建二维码
        if (null == user.getQrcodeUrl() || "".equals(user.getQrcodeUrl().trim())) {
//            QrcodeResponse qrcodeResponse = qrcodeAPI.createQrcode(QrcodeType.QR_SCENE, String.valueOf(user.getId()), 2592000);
            QrcodeResponse qrcodeResponse = qrcodeAPI.createQrcode(QrcodeType.QR_SCENE, String.valueOf(1), 2592000);//todo 换成上面那一句
            String qrcodeUrl = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + qrcodeResponse.getTicket();

            URL srcDir = null;
            try {
                srcDir = new URL(qrcodeUrl);
            } catch (MalformedURLException e) {
                LOG.warn("method: createQrcode(), msg: new a URL object of qrcode failure.", e);
            }

            //获取存放二维码的文件夹
            File destDir = getSubFolder();
            File qrcode = new File(destDir.getPath() + "/" + openid + ".png");

            try {
                FileUtils.copyURLToFile(srcDir, qrcode);
                user.setQrcodeRefreshTime(new Date());
                user.setQrcodeUrl(qrcode.getPath());
                userDao.save(user);
            } catch (IOException e) {
                LOG.warn("method: createQrcode(), msg: copy qrcodeURL to file failure.", e);
            }
        } else {
            refreshQrcode(openid);
        }
    }

    /**
     * 查找存放二维码的文件夹
     * @return
     */
    private File getSubFolder() {
        File baseDir = new File(baseQrcodePath);

        //如果文件夹不存在，则创建一个
        if (!baseDir.exists()) {
            baseDir.mkdirs();
        }

        //计算一共有多少个子文件夹
        int totalSonDir = FileUtil.totalSonDirByFixedPrefix(baseDir, "sub_");
        int latestDirNum = 0;
        File latestDir;

        if (totalSonDir < 1) {//如果还没有子文件夹，则创建第一个，并返回此目录
            latestDir = new File(baseQrcodePath + "/sub_1");
            latestDir.mkdir();
            return latestDir;
        } else {
            latestDirNum = totalSonDir;
            latestDir = new File(baseQrcodePath + "/sub_" + latestDirNum);
            int totalQrcode = FileUtil.totalSonFileByFixedPrefix(latestDir, "");
            if (totalQrcode < 5000) {//如果最新sub文件夹的数量小于5000，直接返回此文件夹
                return latestDir;
            } else {//否则创建新文件夹，并返回此新的文件夹
                latestDirNum++;
                latestDir = new File(baseQrcodePath + "/sub_" + latestDirNum);
                boolean exist = latestDir.mkdir();
                if (!exist) {
                    while (!exist) {
                        latestDirNum++;
                        latestDir = new File(baseQrcodePath + "/sub_" + latestDirNum);
                        exist = latestDir.mkdir();
                    }
                }
                return latestDir;
            }
        }
    }

    @Override
    public void refreshQrcode(String openid) {

        User user = userDao.findOne(openid);

        if (user.getQrcodeUrl() == null || "".equals(user.getQrcodeUrl())) {
            createQrcode(openid);
        } else {

//            QrcodeResponse qrcodeResponse = qrcodeAPI.createQrcode(QrcodeType.QR_SCENE, String.valueOf(user.getId()), 2592000);
            QrcodeResponse qrcodeResponse = qrcodeAPI.createQrcode(QrcodeType.QR_SCENE, String.valueOf(1), 2592000);//换成上面那一句
            String qrcodeUrl = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + qrcodeResponse.getTicket();

            URL srcDir = null;
            try {
                srcDir = new URL(qrcodeUrl);
            } catch (MalformedURLException e) {
                LOG.warn("method: refreshQrcode(), msg: new a URL object of qrcode failure.", e);
            }
            File descDir = new File(user.getQrcodeUrl());
            try {
                FileUtils.copyURLToFile(srcDir, descDir);
                user.setQrcodeRefreshTime(new Date());
                userDao.save(user);
            } catch (IOException e) {
                LOG.warn("method: refreshQrcode(), msg: copy qrcode from weixin to server failure.", e);
            }
        }
    }

    @Override
    public void sign(String openid) {

        //添加签到操作记录到redis中
        BoundZSetOperations ops = redisTemplate.boundZSetOps(RedisKey.SIGN_OPERATION_RECORD);
        ops.add(openid, new Date().getTime());

        /**
         * 判断是否有签到事件锁
         */
        User user = userDao.findOne(openid);
        if (userEventLockDao.countByUserOpenidAndType(openid, UserEventLockType.SIGN) < 1) {
            eventService.addUserEventLock(user, UserEventLockType.SIGN);
        }


        String reMsg = null;
        try {
            //不能直接调用，直接调用signTransaction导致无法启用事务功能
            reMsg = eventService.signTransaction(openid);
        } catch (ObjectOptimisticLockingFailureException e) {
            LOG.info("method login. msg: optimistic lock has work. ", e);
        }

        if (null != reMsg) {
            customAPI.sendCustomMessage(openid, new TextMsg(reMsg));
        }
    }

    /**
     * 签到事务管理（用乐观锁防止重复签到）
     * @param openid
     * @return
     */
    @Override
    @Transactional
    public String signTransaction (String openid) {

        User user = userDao.findOne(openid);

        //重复签到问题，用带版本号的乐观锁解决
//        UserEventLock userEventLock = userEventLockDao.findByUserOpenidAndType(openid, UserEventLockType.SIGN);
        userEventLockDao.findByUserOpenidAndType(openid, UserEventLockType.SIGN);

        //如果今天已经签到
        if (hasSignedToday(openid)) {
            LOG.debug("今天已经签到 today has signed");
            return "亲爱的环球小伙伴，你今天已经签到，每天只能签到一次哦～";
        } else {//如果没有签到
            LOG.debug("今天没有签到 today has not signed");

            Date signTime = new Date();
//            List<Event> events = eventDao.findByKeyOrKeyOrKeyOrderByStartTime(EventType.SIGN_1, EventType.SIGN_2, EventType.SIGN_3);
            List<Event> events = eventDao.findByKeyStartingWithOrderByStartTime(EventType.PREFIX_SIGN);
            SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");

            //取出所有签到事件，如果事件符合当前签到时间，则签到成功
            for (Event event : events) {
                Date startTime = event.getStartTime();
                Date endTime = event.getEndTime();
                long signYcoin;
                long successionSignYcoin = 0;
//                boolean isInTime = TimeUtil.isInTime(sdf.format(startTime) + "-" + sdf.format(endTime), sdf.format(signTime));
                boolean isInTime = TimeUtil.inTime(startTime, endTime, signTime, TimeUtil.DAY_SCOPE);
                if (isInTime) {
                    //设置签到事件参数
                    UserEvent userEvent = new UserEvent(user, event);
                    signYcoin = event.getYcoin();
//                    userEvent.setType(UserEventType.ADD);
                    userEvent.setYcoin(signYcoin);
                    userEvent.setDate(signTime);

                    //获取前一次签到记录
                    Pageable pageable = new PageRequest(0, 1, Sort.Direction.DESC, "date");
                    Page<UserEvent> page = userEventDao.findByUserOpenidAndEventKeyStartingWith(openid, EventType.PREFIX_SIGN, pageable);
                    List<UserEvent> list = page.getContent();
                    if (list.size() > 0) {//如果不是第一次签到
                        UserEvent lastSign = list.get(0);
                        if (TimeUtil.isYesterday(lastSign.getDate())) {//如果昨天签到，则今天签到连续次数+1
                            //存连续签到次数在content中
                            userEvent.setContent(String.valueOf(Integer.parseInt(lastSign.getContent()) + 1));
                        } else {//如果昨天没有签到，则连续签到次数重制为1
                            userEvent.setContent(String.valueOf(1));
                        }
                    } else {//否则说明从没有签过到
                        userEvent.setContent(String.valueOf(1));
                    }

                    List<Event> successionSignEvents = eventDao.findByKeyStartingWithAndStatusOrderByContentDesc(EventType.PREFIX_SUCCESSION_SIGN, EventStatus.ON);

                    //连续签到事件
                    for (Event e : successionSignEvents) {
                        if (Integer.valueOf(e.getContent()) <= Integer.valueOf(userEvent.getContent())) {
                            UserEvent successionSignEvent = new UserEvent(user, e);
                            successionSignYcoin = e.getYcoin();
                            successionSignEvent.setDate(signTime);
                            successionSignEvent.setYcoin(successionSignYcoin);
                            userEventDao.save(successionSignEvent);
                            break;
                        }
                    }

                    //签到事件：添加jms代码，签到和连续签到获取的逸贝合并
                    BaseEventRequest signRequest = new BaseEventRequest();
                    signRequest.setUnionid(user.getUnionid());
                    signRequest.setTimestamp(signTime.getTime());
                    signRequest.setType(EventType.EVENT_SIGN);
                    signRequest.setYcoin(signYcoin + successionSignYcoin);

                    BaseResponse response = null;

                    try {
                        response = JsonUtil.toBean(queueClient.sendAndReceive(signRequest.toString(), JmsDestinationName.EVENT, 20000), BaseResponse.class);
                    } catch (JMSException e) {
                        LOG.warn("method: signTransaction. msg: jms exception.", e);
                    }

                    LOG.info("method: signTransaction. msg: jms sign response\n {}.", response);

                    if (null != response && response.isSuccess()) { //如果逸贝系统返回true
                        userEventDao.save(userEvent);//保存签到事件

                        //把所有签到事件存入list中
//                        List<String> keys = new ArrayList<>();
//                        keys.add(EventType.SIGN_1);
//                        keys.add(EventType.SIGN_2);
//                        keys.add(EventType.SIGN_3);
//                        int totalSign = userEventDao.countByUserOpenidAndEventKeyIn(openid, keys);

                        int totalSign = userEventDao.countByUserOpenidAndEventKeyStartingWith(openid, EventType.PREFIX_SIGN);

                        StringBuffer msg = new StringBuffer();
                        msg.append("亲爱的【")
                                .append(user.getNickname())
                                .append("】同学，你今天已成功在【")
                                .append(sdf.format(startTime) + "-" + sdf.format(endTime))
                                .append("】时段内签到，这是你的第【")
                                .append(totalSign)
                                .append("】天签到。");

                        msg.append("\n--------------------\n你由于早起学习被奖励了【")
                                .append(event.getYcoin())
                                .append("】个逸贝。");

                        msg.append("\n--------------------\n坚持每一天你就会成功哦！");
                        return msg.toString();
                    } else {
                        return "系统开小差了，请稍后再试。";
                    }
                }
            }
            Date start = events.get(0).getStartTime();
            Date end = events.get(events.size() - 1).getEndTime();
            StringBuffer reMsg = new StringBuffer();
            if (TimeUtil.inTime(start, end, signTime, TimeUtil.DAY_SCOPE)) {
                reMsg.append("亲爱的【").append(user.getNickname()).append("】同学，过了凌晨").append(sdf.format(start)).append("才可以签到哦～早点休息，明天争取早起！");
            } else {
                reMsg.append("亲爱的【").append(user.getNickname())
                        .append("】同学，你今天已错过签到时间，签到时间为每日的【")
                        .append(sdf.format(start)).append("-")
                        .append(sdf.format(end)).append("】哦，每天坚持早起可以形成良好的学习习惯哦！好的学习习惯既能提高学习成绩，又能得到诸多环球君免费赠送的“助力学习公益奖品”，")
                        .append("点击<a href=\"http://mp.weixin.qq.com/s/6K4iLMKdvNIuNlKFNI-ABg\">赠品清单</a>即可知道你的逸贝能得到环球君送的什么礼物哦～");
            }
            return reMsg.toString();
        }
    }

    /**
     * 判断今天是否已经签到
     * @param openid
     * @return
     */
    @Override
    @Transactional
    @Cacheable(value = "hasSignedToday", keyGenerator = CacheConst.KEY_GENERATOR)
    public boolean hasSignedToday(String openid) {
//        List<String> keys = new ArrayList<>();
//        keys.add(EventType.SIGN_1);
//        keys.add(EventType.SIGN_2);
//        keys.add(EventType.SIGN_3);

//        Pageable pageable = new PageRequest(0, 1, Sort.Direction.DESC, "date");
//        Page<UserEvent> page = userEventDao.findByUserOpenidAndEventKeyIn(openid, keys, pageable);

//        Page<UserEvent> page = userEventDao.findByUserOpenidAndEventKeyStartingWith(openid, EventType.PREFIX_SIGN, pageable);
//
//        List<UserEvent> list = page.getContent();
//        if (list.size() > 0) {
//            UserEvent userEvent = list.get(0);
//            if (TimeUtil.isToday(userEvent.getTimestamp())) {
//                LOG.debug("method: hasSignedToday. msg: the user has signed today(今天已经签过到了). openid: {}", openid);
//                return true;
//            } else {
//                LOG.debug("method: hasSignedToday. msg: first time to sign today(今天第一次签到). openid: {}", openid);
//                return false;
//            }
//        } else {
//            LOG.debug("method: hasSignedToday. msg: first time to sign in his life(人生第一次签到). openid: {}", openid);
//            return false;
//        }
        return false;//todo 判断签到条件临时改变，方便测试
//        return userEventDao.countByUserOpenidAndDateGreaterThanAndEventKeyStartingWith(openid, TimeUtil.startOfDateBaseNow(0), EventType.PREFIX_SIGN) > 0 ? true : false;
    }

    /**
     * 添加事件锁 （用串行的方式运行，保证只添加一次）
     * @param user
     * @param type
     */
    @Override
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void addUserEventLock (User user, UserEventLockType type) {
        if (userEventLockDao.countByUserOpenidAndType(user.getOpenid(), type) < 1) {
            UserEventLock lock = new UserEventLock(user, type);
            userEventLockDao.save(lock);
            LOG.info("method: addUserEventLock. msg: successfully!");
        }
    }

    @Override
    @Cacheable(value = "isQrcodeLegal", keyGenerator = CacheConst.KEY_GENERATOR)
    public boolean isQrcodeLegal(String openid) {
        Date qrcodeRefreshTime = userDao.findOne(openid).getQrcodeRefreshTime();
        if (null == qrcodeRefreshTime) {//如果刷新时间为null，则设置为1970-01-01 08:00:00
            return false;
        }
        return (new Date().getTime() - qrcodeRefreshTime.getTime() < 2588400000L);
    }

    @Override
    @Cacheable(value = "isSubscribe", keyGenerator = CacheConst.KEY_GENERATOR)
    public boolean isSubscribe(String openid) {
        return userDao.countByOpenid(openid) > 0;
    }

    @Value("${baseQrcodePath}")
    private String baseQrcodePath;

    @Autowired
    private UserDao userDao;

    @Autowired
    private EventDao eventDao;

    @Autowired
    private UserEventDao userEventDao;

    @Autowired
    private UserEventLockDao userEventLockDao;

    @Autowired
    private EventService eventService;

    @Autowired
    private UserService userService;

    @Autowired
    private UserAPI userAPI;

    @Autowired
    private QrcodeAPI qrcodeAPI;

    @Autowired
    private CustomAPI customAPI;

    @Autowired
    private QueueClient queueClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private JmsTemplate jmsTemplate;
}
